// 首先加载模块解析器，确保在打包环境中能找到正确的依赖
// 注意：由于这里使用ES模块，我们需要一种不同的方式来处理路径
// 我们可以用动态导入来解决这个问题
;(async () => {
  try {
    // 路径处理需要Node.js内置模块
    const path = await import("path")
    const { fileURLToPath } = await import("url")

    // 获取当前文件的目录
    const __filename = fileURLToPath(import.meta.url)
    const __dirname = path.dirname(__filename)

    // 确定模块解析器的路径
    let moduleResolverPath
    if (process.env.IS_PACKAGED === "true") {
      // 在生产环境，解析器应该在同一目录
      moduleResolverPath = path.join(__dirname, "module-resolver.cjs")
    } else {
      // 开发环境，使用相对路径
      moduleResolverPath = "./module-resolver.cjs"
    }

    // 记录关键路径以便调试
    console.log("[自动化脚本] 当前目录:", __dirname)
    console.log("[自动化脚本] 模块解析器路径:", moduleResolverPath)

    // 使用CommonJS require()加载模块解析器
    // 这需要使用createRequire API
    const { createRequire } = await import("module")
    const require = createRequire(import.meta.url)

    // 加载并初始化模块解析器
    const { setupModulePaths } = require(moduleResolverPath)
    setupModulePaths()
    console.log("[自动化脚本] 模块解析器已初始化")
  } catch (err) {
    console.error("[自动化脚本] 初始化模块解析器失败:", err)
    // 继续执行，但可能会在后续导入时失败
  }
})()

import "./utils/log.js"
import { windowsData } from "./config/index.js"
// 使用我们的适配器而不是直接导入puppeteer
import { StealthPlugin, puppeteer_extra } from "./utils/puppeteer-adapter.js"
import { mqttService } from "./mqtt/index.js"
import { getTaskPolicy } from "./api/webApi/index.js"
import { getWindowStatuses, sendAutomationMessage, stopTask } from "./utils/index.js"
import { adsConfig } from "./config/index.js"
import { windowIdOfTask, params, generateAdsConfig } from "./config/index.js"
import { td_bindAccount, fb_bindAccount, tk_bindAccount } from "./tasks/index.js"
import { closeWindowData } from "./api/adsApi/index.js"
import { taskScheduler, updateTaskPolicy } from "./utils/taskScheduler.js"
// 全局配置信息
const { mode } = params
const env = {
  ["development"]: "开发环境",
  ["test"]: "测试环境",
  ["production"]: "生产环境",
}

console.log(`[自动化服务] 启动模式: ${mode}`)
console.warn("当前运行环境：====>", env[mode])
generateAdsConfig(windowsData.deviceIdList, mode)

// 缓存windowsData以便后续使用
let cachedWindowsData = JSON.parse(JSON.stringify(windowsData))

// 连接 MQTT
try {
  console.log("[自动化服务] 正在连接MQTT服务...")
  mqttService.connect()

  // 使用 onMessage 方法注册消息处理函数
  mqttService.onMessage(async (topic, mqttMessage, packet) => {
    try {
      const responseTopic = packet?.properties?.responseTopic
      console.log(`[自动化服务] 收到MQTT消息: ${topic}`)
      // 收到mqtt之后。更新对应的调度器
      // 因为从主进程传过来的消息已经被解析，所以不需要再检查 instanceof Uint8Array
      let originalData
      if (typeof mqttMessage === "string") {
        try {
          originalData = JSON.parse(mqttMessage)
        } catch (e) {
          originalData = mqttMessage
        }
      } else {
        originalData = mqttMessage
      }

      console.log("收到 MQTT 数据集", originalData?.data)
      // 获取到要重启什么
      if (originalData?.data && originalData?.data.restartType == "phone") {
        // 更新对应的窗口任务调度器
        console.log("收到重启设备指令，更新任务调度器")
        // 获取指定id
        const { deviceId, tenantId } = originalData
        // 首先检查windowsData.data是否为空，如果为空则使用缓存数据
        const currentData = windowsData.data && windowsData.data.length > 0 ? windowsData.data : cachedWindowsData.data || []

        console.log("当前使用的数据来源:", windowsData.data && windowsData.data.length > 0 ? "原始windowsData" : "缓存数据")
        console.log("windowsData.data=====>", currentData)
        // 根据当前的设备id遍历出对应的窗口。获取数据
        const deviceData = currentData.find((v) => v.deviceId == deviceId)
        console.log("deviceData===>", deviceData)
        if (deviceData) {
          // 更新整个窗口下所有账号的任务策略
          for (let index = 0; index < deviceData.platformAccounts.length; index++) {
            const platformAccount = deviceData.platformAccounts[index]?.platformAccount
            // 通过接口调用。获取最新策略
            const taskPolicyResponse = await getTaskPolicy(
              { platformAccount },
              {
                headers: {
                  "Content-Type": "application/json",
                  "X-Platform": deviceData.platformAccounts[index]?.platformName,
                  "X-Device-Id": deviceId,
                  "tenant-id": deviceData.tenantId,
                },
              }
            )
            console.log("taskPolicyResponse===>", taskPolicyResponse.data)
            const params = {
              ...taskPolicyResponse.data,
              deviceId: deviceId,
              deptId: Number(deviceData.deptId),
              taskId: deviceData.taskId,
              host: deviceData.host,
              platformName: platformAccount.platformName,
              tenantId: Number(deviceData.tenantId),
            }
            console.log("params===>", params)
            updateTaskPolicy(params)
          }
        } else {
          console.warn(`[自动化服务] 未找到设备ID为 ${deviceId} 的设备数据，无法更新任务策略`)
        }
      }
    } catch (error) {
      console.error(`[自动化服务] 处理MQTT消息时出错:`, error)
    }
  })
} catch (error) {
  console.error("[自动化服务] MQTT服务连接失败:", error)
  // 继续执行，不要因为MQTT连接失败而中断整个服务
}
// 捕获 Node.js 未处理的异常，防止崩溃
process.on("uncaughtException", (error) => {
  console.error("Uncaught Exception:===>", error)
  // 在这里可以选择记录日志或者发送错误报告等
  // 你也可以选择结束进程并重启
  // 不要尝试写入stdout或stderr，因为可能导致EPIPE错误
  try {
    // 可以尝试写入日志文件
    const fs = require("fs")
    const path = require("path")
    const logDir = path.join(process.cwd(), "logs")

    // 确保日志目录存在
    if (!fs.existsSync(logDir)) {
      fs.mkdirSync(logDir, { recursive: true })
    }

    const logFile = path.join(logDir, `error-${new Date().toISOString().replace(/:/g, "-")}.log`)
    fs.writeFileSync(logFile, `[${new Date().toISOString()}] Uncaught Exception: ${error.stack || error.message}\n`, { flag: "a" })
  } catch (logError) {
    // 如果连日志都无法写入，就静默失败
  }
})

// 捕获 Node.js 未处理的 Promise 拒绝
process.on("unhandledRejection", (error) => {
  console.error("Unhandled Rejection:===>", error)
  // 同样，你可以在这里记录日志或者做其他处理
  try {
    // 可以尝试写入日志文件
    const fs = require("fs")
    const path = require("path")
    const logDir = path.join(process.cwd(), "logs")

    // 确保日志目录存在
    if (!fs.existsSync(logDir)) {
      fs.mkdirSync(logDir, { recursive: true })
    }

    const logFile = path.join(logDir, `error-${new Date().toISOString().replace(/:/g, "-")}.log`)
    fs.writeFileSync(logFile, `[${new Date().toISOString()}] Unhandled Rejection: ${error.stack || error.message}\n`, { flag: "a" })
  } catch (logError) {
    // 如果连日志都无法写入，就静默失败
  }
})

// 捕获停止信号
process.on("SIGINT", async () => {
  console.log(`[主进程] 正在停止...`)
  await closeAllBrowserWindows()
  process.exit(0)
})

// 捕获 SIGTERM 信号
process.on("SIGTERM", async () => {
  console.log(`[主进程] 收到终止信号，正在停止...`)
  await closeAllBrowserWindows()
  process.exit(0)
})

// 添加 SIGUSR1 信号处理（用于自定义的"关闭所有浏览器"指令）
process.on("SIGUSR1", async () => {
  console.log(`[主进程] 收到 SIGUSR1 信号，正在关闭所有浏览器窗口...`)
  await closeAllBrowserWindows()
  // 不终止进程，只关闭窗口
})

// 处理来自主进程的标准输入消息
process.stdin.on("data", async (data) => {
  try {
    const message = JSON.parse(data.toString().trim())
    console.log(`[自动化服务] 收到主进程消息:`, message)

    if (message.command === "close-all-browsers") {
      console.log("[自动化服务] 正在关闭所有浏览器窗口...")
      await closeAllBrowserWindows()
    }
  } catch (error) {
    console.error("[自动化服务] 处理主进程消息时出错:", error)
  }
})

// 防止进程在标准输入关闭时退出
// 确保不会阻塞读取
process.stdin.resume()
process.stdin.setEncoding("utf8")

// 配置
const config = {
  maxWindows: 100, // 最大窗口数量
  maxAccountsPerWindow: 1, // 每个窗口最多处理的账号数
}

// 使用 Stealth 插件规避检测
puppeteer_extra.use(StealthPlugin())

/**
 * 动态分配任务到窗口
 * @param {Array} tasks 原始任务数组
 * @param {Array} windowStatuses 窗口运行状态，包含窗口编号和是否空闲
 * @param {number} maxAccountsPerWindow 每个窗口最大任务数
 * @returns {Object[]} 分配后的任务数组和未分配任务
 */
function allocateTasksDynamically(tasks, windowStatuses, maxAccountsPerWindow) {
  console.log("windowStatuses===windowStatuses", windowStatuses)
  const freeWindows = windowStatuses.filter((w) => w.isFree)
  const freeCount = freeWindows.length

  if (freeCount === 0) {
    console.warn("所有窗口均在运行，任务无法分配。")
    return { allocatedTasks: [], remainingTasks: tasks }
  }

  const allocatedTasks = Array(windowStatuses.length)
    .fill()
    .map(() => [])

  if (freeCount === 1) {
    // 如果只有一个窗口空闲，将所有任务分配给该窗口
    const freeWindowIndex = freeWindows[0].windowIndex
    allocatedTasks[freeWindowIndex] = tasks
    return { allocatedTasks, remainingTasks: [] }
  }

  // 多个窗口空闲，平均分配任务
  let taskIndex = 0
  while (tasks.length > 0) {
    const windowIndex = freeWindows[taskIndex % freeCount].windowIndex
    if (allocatedTasks[windowIndex].length < maxAccountsPerWindow) {
      allocatedTasks[windowIndex].push(tasks.shift())
    }
    taskIndex++
  }

  return { allocatedTasks, remainingTasks: [] }
}

// 添加重试函数
async function retryOperation(operation, maxRetries = 3, delay = 1000) {
  let lastError
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await operation()
    } catch (error) {
      lastError = error
      console.error(`操作失败 (尝试 ${i + 1}/${maxRetries}):`, error.message)
      if (i < maxRetries - 1) {
        console.log(`等待 ${delay}ms 后重试...`)
        await new Promise((resolve) => setTimeout(resolve, delay))
      }
    }
  }
  throw lastError
}

// 添加API请求限流控制
export const apiRateLimiter = {
  lastRequestTime: 0,
  minInterval: 1000, // 最小间隔1秒
  queue: [],
  isProcessing: false,

  async request(fn) {
    return new Promise((resolve, reject) => {
      // 将请求加入队列
      this.queue.push({ fn, resolve, reject })

      // 如果没有正在处理的请求，开始处理队列
      if (!this.isProcessing) {
        this.processQueue()
      }
    })
  },

  async processQueue() {
    if (this.queue.length === 0) {
      this.isProcessing = false
      return
    }

    this.isProcessing = true
    const now = Date.now()
    const timeToWait = Math.max(0, this.lastRequestTime + this.minInterval - now)

    if (timeToWait > 0) {
      await new Promise((resolve) => setTimeout(resolve, timeToWait))
    }

    const { fn, resolve, reject } = this.queue.shift()
    this.lastRequestTime = Date.now()

    try {
      const result = await fn()
      resolve(result)
    } catch (error) {
      reject(error)
    }

    // 处理下一个请求
    this.processQueue()
  },
}

// 检查浏览器活动状态的函数，使用限流控制
async function checkBrowserActive(userId) {
  return apiRateLimiter.request(async () => {
    console.log(`检查可用窗口: user_id=${userId}`)
    try {
      const checkResponse = await fetch(`${windowsData.adsApiUrl}/api/v1/browser/active?user_id=${userId}`)
      const responseText = await checkResponse.text()
      console.log(`AdsPower服务检查结果: ${responseText}`)
      return { response: checkResponse, text: responseText }
    } catch (error) {
      console.error(`AdsPower服务不可用: ${error.message}`)
      throw error
    }
  })
}
/**
 * 启动窗口
 * @param {*} windowsData 手动触发数据
 */
export async function openWindows(windowsData) {
  try {
    if (windowsData) {
      if (windowsData.data) {
        // 获取窗口运行状态
        const windowStatuses = await getWindowStatuses(config, mode, adsConfig)
        console.log("处理后的任务数据", windowsData.data)
        const totalAccounts = windowsData.data.length
        console.log(`总任务数量: ${totalAccounts}`)
        // 分配任务
        const { allocatedTasks, remainingTasks } = allocateTasksDynamically(windowsData.data, windowStatuses, config.maxAccountsPerWindow)

        if (remainingTasks.length > 0) {
          console.warn(`有 ${remainingTasks.length} 个任务未分配，因为所有窗口已在运行。`)
        }
        // 去掉空数组
        const newAllocatedTasks = allocatedTasks.filter((task) => task.length > 0)
        console.log("分配后的任务数据", newAllocatedTasks)
        // 启动任务
        try {
          const taskResults = await Promise.all(
            newAllocatedTasks.map(async (tasks, index) => {
              if (windowsData.action === "bindAccount") {
                // 如果是绑定账号，执行绑定账号业务
                console.log(`[窗口 ${index + 1}] 开始运行绑定业务账号任务`)
                // 需要判断不同平台。进行不同的也无账号绑定逻辑
                if (tasks[0]?.platformName === "Threads") {
                  try {
                    const result = await td_bindAccount(tasks, index, 0)
                    console.log(`[窗口 ${index + 1}] 绑定账号任务成功: ${result?.message || "完成"}`)
                    return { success: true, result }
                  } catch (error) {
                    console.error(`[窗口 ${index + 1}] 绑定账号任务失败:`, error?.message || error)
                    return { success: false, error }
                  }
                } else if (tasks[0]?.platformName === "Facebook") {
                  try {
                    const result = await fb_bindAccount(tasks, index, 0)
                    console.log(`[窗口 ${index + 1}] 绑定账号任务成功: ${result?.message || "完成"}`)
                    return { success: true, result }
                  } catch (error) {
                    console.error(`[窗口 ${index + 1}] 绑定账号任务失败:`, error?.message || error)
                    return { success: false, error }
                  }
                } else if (tasks[0]?.platformName === "TikTok") {
                  try {
                    const result = await tk_bindAccount(tasks, index, 0)
                    console.log(`[窗口 ${index + 1}] 绑定账号任务成功: ${result?.message || "完成"}`)
                    return { success: true, result }
                  } catch (error) {
                    console.error(`[窗口 ${index + 1}] 绑定账号任务失败:`, error?.message || error)
                    return { success: false, error }
                  }
                }
              } else if (windowsData.action === "runTask") {
                // 运行普通任务代码不变
                console.log("开始运行业务任务===>")
                // 待执行的账号、窗口、对应的任务策略
                let tasksOfRuns = []
                // 遍历获取对应账号的任务策略
                for (let index = 0; index < tasks.length; index++) {
                  const task = tasks[index]
                  // 请求接口获取任务策略
                  try {
                    // 遍历窗口所有的账号进行业务账号注册到调度器
                    for (let index = 0; index < task.platformAccounts.length; index++) {
                      const platformAccount = task.platformAccounts[index]?.platformAccount
                      const taskPolicyResponse = await getTaskPolicy(
                        { platformAccount },
                        {
                          headers: {
                            "Content-Type": "application/json",
                            "X-Platform": task.platformAccounts[index]?.platformName,
                            "X-Device-Id": task.deviceId,
                            "tenant-id": task.tenantId,
                          },
                        }
                      )
                      console.log("taskPolicyResponse===>", taskPolicyResponse.data)
                      if (tasksOfRuns.findIndex((v) => v.platformAccount == task.account) == -1 && taskPolicyResponse.data) {
                        // 不存在就添加
                        tasksOfRuns.push({
                          ...taskPolicyResponse.data,
                          deviceId: task.deviceId,
                          deptId: Number(task.deptId),
                          taskId: task.taskId,
                          host: task.host,
                          platformName: task.platformAccounts[index]?.platformName,
                          tenantId: Number(task.tenantId),
                        })
                      }
                      await new Promise((resolve) => setTimeout(resolve, 200))
                      console.log("tasksOfRuns===>", tasksOfRuns)
                    }
                    if (tasksOfRuns.length != 0) {
                      // 启动调度器
                      taskScheduler.startScheduler(tasksOfRuns)
                    }
                  } catch (error) {
                    console.error("获取任务策略失败:" + task.account, error)
                  }
                }
              }
            })
          )

          // 统计成功和失败的任务数
          const successTasks = taskResults.filter((r) => r && r.success).length
          const failedTasks = taskResults.filter((r) => r && !r.success).length

          console.log(`所有窗口启动完成，成功: ${successTasks}, 失败: ${failedTasks}`)

          // 发送任务完成消息
          if (windowsData.action === "bindAccount") {
            const status = failedTasks === 0 ? "success" : successTasks > 0 ? "partial" : "failed"
            sendAutomationMessage("bind-account-done", status)
          }
        } catch (error) {
          console.error("任务执行过程中出错:", error)
          if (windowsData.action === "bindAccount") {
            sendAutomationMessage("bind-account-done", "failed")
          } else if (windowsData.action === "runTask") {
            sendAutomationMessage("run-task-done", "failed")
          }
        }
      }
    }
  } catch (error) {
    console.error("处理 MQTT 消息时出错:", error)
    // 出错时也尝试关闭所有浏览器窗口
    try {
      await closeAllBrowserWindows()
    } catch (closeError) {
      console.error("关闭浏览器窗口出错:", closeError)
    }
    if (windowsData.action === "bindAccount") {
      sendAutomationMessage("bind-account-done", "failed")
    } else if (windowsData.action === "runTask") {
      sendAutomationMessage("run-task-done", "failed")
    }
  }
}

/**
 * 关闭所有浏览器窗口
 */
async function closeAllBrowserWindows() {
  console.log(`[自动化服务] 开始关闭所有浏览器窗口...`)

  // 跟踪已关闭的窗口ID，避免重复关闭
  const closedWindowIds = new Set()

  try {
    // 1. 先尝试停止任务调度器中的所有任务，它会关闭相关窗口
    if (typeof taskScheduler.stopAllTasks === "function") {
      console.log(`[自动化服务] 停止所有任务调度...`)
      await taskScheduler.stopAllTasks()
    }

    // 2. 获取所有任务ID并关闭相关窗口
    const allTaskIds = Object.keys(windowIdOfTask)

    if (allTaskIds.length === 0) {
      console.log(`[自动化服务] 没有需要关闭的浏览器窗口，尝试强制搜索所有活动窗口...`)

      // 如果没有在 windowIdOfTask 中找到窗口，可能是因为窗口ID没有正确注册
      // 特别是在绑定流程中，尝试关闭所有活动的浏览器窗口
      if (windowsData && windowsData.data && Array.isArray(windowsData.data)) {
        // 从传入的窗口数据中提取设备ID
        const deviceIds = windowsData.data.map((item) => item.deviceId).filter(Boolean)
        console.log(`[自动化服务] 从窗口数据中找到 ${deviceIds.length} 个设备ID`, deviceIds)

        if (deviceIds.length > 0) {
          const closePromises = deviceIds.map((windowId) => {
            return new Promise(async (resolve) => {
              try {
                console.log(`[自动化服务] 尝试关闭窗口 ID: ${windowId}`)
                const response = await closeWindowData({ windowId })
                closedWindowIds.add(windowId)
                console.log(`[自动化服务] 成功关闭窗口 ID: ${windowId}`)
                resolve(true)
              } catch (error) {
                console.error(`[自动化服务] 关闭窗口 ${windowId} 时出错:`, error)
                resolve(false)
              }
            })
          })

          // 等待所有关闭操作完成
          await Promise.all(closePromises)
          console.log(`[自动化服务] 完成所有窗口关闭尝试，共关闭 ${closedWindowIds.size} 个窗口`)
          console.log(`[自动化服务] 所有浏览器窗口已关闭`)
          return
        }
      }

      // 即使没有找到窗口也发送完成消息
      console.log(`[自动化服务] 所有浏览器窗口已关闭`)
      return
    }

    // 对每个任务调用stopTask来关闭相关窗口
    const closePromises = allTaskIds.map((taskId) => {
      return new Promise(async (resolve) => {
        try {
          // 获取当前任务的窗口ID，避免已关闭的窗口
          const windowIdsToClose = (windowIdOfTask[taskId] || []).filter((id) => !closedWindowIds.has(id))

          if (windowIdsToClose.length === 0) {
            console.log(`[自动化服务] 任务 ${taskId} 没有窗口需要关闭`)
            resolve()
            return
          }

          console.log(`[自动化服务] 关闭任务 ${taskId} 的窗口: ${windowIdsToClose.join(", ")}`)

          await new Promise((resolveTask) => {
            stopTask(taskId, null, () => {
              console.log(`[自动化服务] 任务 ${taskId} 的所有窗口已关闭`)
              // 记录已关闭的窗口
              windowIdsToClose.forEach((id) => closedWindowIds.add(id))
              resolveTask()
            })
          })
          resolve()
        } catch (error) {
          console.error(`[自动化服务] 关闭任务 ${taskId} 的窗口时出错:`, error)
          resolve() // 即使出错也要继续处理其他任务
        }
      })
    })

    // 等待所有关闭操作完成
    await Promise.all(closePromises)

    // 3. 额外保障措施：直接循环关闭所有记录的窗口ID，但只处理未关闭的窗口
    for (const taskId in windowIdOfTask) {
      const windowIds = (windowIdOfTask[taskId] || []).filter((id) => !closedWindowIds.has(id))
      if (windowIds.length === 0) continue

      console.log(`[自动化服务] 直接关闭任务 ${taskId} 的剩余窗口: ${windowIds.join(", ")}`)

      for (const windowId of windowIds) {
        try {
          console.log(`[自动化服务] 直接尝试关闭窗口 ID: ${windowId}`)
          const response = await closeWindowData({ windowId })
          if (response && response.code !== -1) {
            closedWindowIds.add(windowId)
            console.log(`[自动化服务] 成功关闭窗口 ID: ${windowId}`)
          } else {
            console.log(`[自动化服务] 关闭窗口 ID: ${windowId} 返回结果:`, response)
          }
        } catch (error) {
          console.error(`[自动化服务] 关闭窗口 ${windowId} 时出错:`, error)
        }
      }
      // 清空该任务的窗口记录
      windowIdOfTask[taskId] = []
    }

    // 发送明确的完成消息，确保主进程能识别
    console.log(`[自动化服务] 所有浏览器窗口已关闭，共处理 ${closedWindowIds.size} 个窗口`)
  } catch (error) {
    console.error(`[自动化服务] 关闭浏览器窗口过程中发生错误:`, error)
    // 即使出错也要发送完成消息，确保主进程不会无限等待
    console.log(`[自动化服务] 所有浏览器窗口已关闭`)
  }
}

console.log("服务已启动，等待 MQTT 消息...")
// 启动窗口
await openWindows(windowsData)

